package myTestPack;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import tr.gov.tubitak.uekae.esya.api.asn.x509.ECertificate;
import tr.gov.tubitak.uekae.esya.api.common.util.LicenseUtil;
import tr.gov.tubitak.uekae.esya.api.crypto.util.KeyUtil;
import tr.gov.tubitak.uekae.esya.api.signature.SignatureType;
import tr.gov.tubitak.uekae.esya.api.smartcard.example.JSmartCardManager;
import tr.gov.tubitak.uekae.esya.api.xades.example.utils.SampleBase;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.Context;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.DigestMethod;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.SignatureMethod;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.TransformType;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.ValidationResult;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.XMLSignature;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.XMLSignatureException;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.config.Config;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.document.FileDocument;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.Transform;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.Transforms;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.keyinfo.KeyValue;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.xades.ClaimedRole;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.xades.SignerRole;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.util.XmlUtil;

public class ArchieveSignatureTests extends SampleBase {

	public static final String SIGNATURE_FILENAME = "imzali_fatura.xml";
	public static final String SIGNED_FILENAME = "imzali_fatura.xml";

	private static XMLGregorianCalendar getTime() {
		Calendar cal = new GregorianCalendar();
		cal.get(Calendar.SECOND);
		return XmlUtil.createDate(cal);
	}

	public static void main(String[] args) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss:sss");
		try {
			System.out.println(dateFormat.format(new Date()));
			InputStream inputStream = new FileInputStream(ROOT_DIR + "/lisans/Full_lisans.xml");
			LicenseUtil.setLicenseXml(inputStream);

			ArchieveSignatureTests tests = new ArchieveSignatureTests();
			tests.testUblArchieveSignature();
			// tests.createEnveloped();
			System.out.println(dateFormat.format(new Date()));
		} catch (Exception e) {
			System.err.println("Erron when signing:" + e.toString());
		}

		try {
			testValidation();
		} catch (Exception e) {
			System.err.println("Error when validating:" + e.toString());
		}
	}

	public void testArchieveSignature() throws Exception {
		try {
			// here is our custom envelope XML
			Document envelopeDoc = newUnsignedUblInvoice();

			// create context with working directory
			Context context = createContext();

			// define where signature belongs to
			context.setDocument(envelopeDoc);

			// add resolver to resolve policies
			context.addExternalResolver(POLICY_RESOLVER);

			// create signature according to context,
			// with default type (XADES_BES)
			XMLSignature signature = new XMLSignature(context, false);

			// attach signature to envelope
			envelopeDoc.getDocumentElement().appendChild(signature.getElement());

			// add document as reference,
			String nedir = signature.addDocument("#data1", "text/xml", false);
			System.out.println(nedir);
			signature.getSignedInfo().setSignatureMethod(SignatureMethod.RSA_SHA256);

			// false-true gets non-qualified certificates while true-false gets
			// qualified ones
			X509Certificate cert = JSmartCardManager.getInstance().getSignatureCertificate(false, true);

			// add certificate to show who signed the document
			signature.addKeyInfo(new ECertificate(cert.getEncoded()));

			// set time now
			signature.setSigningTime(Calendar.getInstance());

			// set policy info defined and required by profile
			signature.setPolicyIdentifier(OID_POLICY_P4, "Uzun Dönemli ve ÇİSDuP Kontrollü Güvenli Elektronik İmza Politikası",
					"http://www.tk.gov.tr/bilgi_teknolojileri/elektronik_imza/dosyalar/Elektronik_Imza_Kullanim_Profilleri_Rehberi.pdf");

			// now sign it by using smart card
			signature.sign(JSmartCardManager.getInstance().getSigner(PIN, cert));

			// upgrade to XL
			signature.upgrade(SignatureType.ES_XL);

			// xuxux - upgrade to A
			signature.upgradeToXAdES_A();
			// signature.addArchiveTimeStamp();

			// this time we do not use signature.write because we need to write
			// whole document instead of signature
			Source source = new DOMSource(envelopeDoc);
			Transformer transformer = TransformerFactory.newInstance().newTransformer();

			// write to file
			transformer.transform(source, new StreamResult(new FileOutputStream(BASE_DIR + SIGNATURE_FILENAME)));

		} catch (XMLSignatureException x) {
			x.printStackTrace();
		} catch (IOException x) {
			// probably couldn't write to the file
			x.printStackTrace();
		}
	}

	public void testUblArchieveSignature() throws Exception {
		try {
			// read XML file from document
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setNamespaceAware(true);
			DocumentBuilder db = dbf.newDocumentBuilder();
			// read from file, it can be stream too
			org.w3c.dom.Document faturaDoc = db.parse(new File(BASE_DIR + "imzasizfatura.xml"));

			// get the element that signature will be added
			Element extContent = (Element) faturaDoc.getDocumentElement().getElementsByTagName("ext:ExtensionContent").item(0);

			// generate signature
			Context context = createContext();
			context.setDocument(faturaDoc);
			XMLSignature signature = new XMLSignature(context, false);

			// attach signature to envelope structure
			extContent.appendChild(signature.getElement());

			// use enveloped signature transform
			Transforms transforms = new Transforms(context);
			transforms.addTransform(new Transform(context, TransformType.ENVELOPED.getUrl()));

			// add whole document(="") with envelope transform, with SHA256
			// and don't include it into signature(false)
			signature.addDocument("", "text/xml", transforms, DigestMethod.SHA_256, false);

			signature.getSignedInfo().setSignatureMethod(SignatureMethod.RSA_SHA256);

			// false-true gets non-qualified certificates while true-false gets
			// qualified ones
			X509Certificate cert = JSmartCardManager.getInstance().getSignatureCertificate(false, true);

			// add certificate to show who signed the document
			signature.addKeyInfo(new ECertificate(cert.getEncoded()));

			// add signer role information
			SignerRole rol = new SignerRole(context, new ClaimedRole[] { new ClaimedRole(context, "Supplier") });
			signature.getQualifyingProperties().getSignedSignatureProperties().setSignerRole(rol);

			// e-fatura standards want public key info to be in the signature
			PublicKey pk = KeyUtil.decodePublicKey(new ECertificate(cert.getEncoded()).getSubjectPublicKeyInfo());
			signature.getKeyInfo().add(new KeyValue(context, pk));

			// add signing time
			signature.getQualifyingProperties().getSignedSignatureProperties().setSigningTime(getTime());

			/**
			 * xuxux
			 */
			// set policy info defined and required by profile
//			signature.setPolicyIdentifier(OID_POLICY_P4, "Uzun Dönemli ve ÇİSDuP Kontrollü Güvenli Elektronik İmza Politikası",
//					"http://www.tk.gov.tr/bilgi_teknolojileri/elektronik_imza/dosyalar/Elektronik_Imza_Kullanim_Profilleri_Rehberi.pdf");

			signature.sign(JSmartCardManager.getInstance().getSigner(PIN, cert));

			// upgrade to XL
			signature.upgrade(SignatureType.ES_XL);

			// xuxux - upgrade to A
			signature.upgradeToXAdES_A();
			// signature.addArchiveTimeStamp();

			// e-fatura standards want signatureID to be same with cbc:URI
			// get signatureID from e-fatura
			String signatureID = ((Element) (faturaDoc.getDocumentElement().getElementsByTagName("cbc:URI").item(0))).getTextContent();
			String signatureIDwoNumberSign = signatureID.substring(1);

			// change original signatureID
			Element dsSignature = (Element) (faturaDoc.getDocumentElement().getElementsByTagName("ds:Signature").item(0));
			dsSignature.setAttribute("Id", signatureIDwoNumberSign);

			Element xadesQualifyingProperties = (Element) (faturaDoc.getDocumentElement().getElementsByTagName("xades:QualifyingProperties").item(0));
			xadesQualifyingProperties.setAttribute("Target", signatureID);

			// write to file
			Source source = new DOMSource(faturaDoc);
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.transform(source, new StreamResult(new FileOutputStream(BASE_DIR + SIGNATURE_FILENAME)));

		} catch (XMLSignatureException x) {
			x.printStackTrace();
		} catch (IOException x) {
			// probably couldn't write to the file
			x.printStackTrace();
		}

	}

	public void createEnveloped() throws Exception {
		// here is our custom envelope XML
		Document envelopeDoc = newEnvelope();

		// create context with working directory
		Context context = createContext();

		// define where signature belongs to
		context.setDocument(envelopeDoc);

		// create signature according to context,
		// with default type (XADES_BES)
		XMLSignature signature = new XMLSignature(context, false);

		// attach signature to envelope
		envelopeDoc.getDocumentElement().appendChild(signature.getElement());

		// add document as reference,
		signature.addDocument("#data1", "text/xml", false);

		signature.getSignedInfo().setSignatureMethod(SignatureMethod.RSA_SHA256);

		// false-true gets non-qualified certificates while true-false gets
		// qualified ones
		X509Certificate cert = JSmartCardManager.getInstance().getSignatureCertificate(true, false);

		// add certificate to show who signed the document
		signature.addKeyInfo(new ECertificate(cert.getEncoded()));

		// now sign it by using smart card
		signature.sign(JSmartCardManager.getInstance().getSigner(PIN, cert));

		// this time we do not use signature.write because we need to write
		// whole document instead of signature
		Source source = new DOMSource(envelopeDoc);
		Transformer transformer = TransformerFactory.newInstance().newTransformer();

		// write to file
		transformer.transform(source, new StreamResult(new FileOutputStream(BASE_DIR + SIGNATURE_FILENAME)));
	}

	public static void testValidation() throws Exception {
		try {
			InputStream inputStream = new FileInputStream(ROOT_DIR + "/lisans/Full_lisans.xml");
			LicenseUtil.setLicenseXml(inputStream);

			InputStream configInputStream = new FileInputStream("D:/Workspaces/kepler/testsForSigner/MA3-JAVA API/config/xmlsignature-config.xml");
			Config conf = new Config(configInputStream);

			// create context with working directory
			Context context = new Context("D:/Workspaces/kepler/testsForSigner/MA3-JAVA API/testdata/testdata");
			context.setConfig(conf);

			XMLSignature signature = XMLSignature.parse(new FileDocument(new File("D:/Workspaces/kepler/testsForSigner/MA3-JAVA API/testdata/"
					+ SIGNED_FILENAME)), context);
			// no params, use the certificate in key info
			ValidationResult result = signature.verify();

			System.out.println(result.toXml());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}
